#include "IniManager.h"
#include <sstream>

static wstring m_fileName;

bool IniManager::TryUpdateKey(const string& value, const string& fileName)
{
	return WritePrivateProfileStringW(L"USER", L"key", _W(value).c_str(), _W(fileName).c_str()) != 0;
}

// Read the given ini file.
bool IniManager::Read(const string& fileName, Configuration& configuration)
{
	m_fileName = _W(fileName);
	return TryReadString("USER", "name", "UserName", configuration.ConnectionOptions.UserName) &&
		TryReadString("USER", "key", "UserKey", configuration.ConnectionOptions.UserKey) &&
		TryReadCoordinates("GRABBER", "Coordinates", "915 - 757", configuration.GrabOptions.InputOptions.clickCoordinates) &&
		TryReadInteger("GRABBER", "StartAfterInterval", 0, configuration.GrabOptions.StartAfterInterval) &&
		TryReadInteger("GRABBER", "StartAfterRandomInterval", 0, configuration.GrabOptions.StartAfterRandomInterval) &&
		TryReadInteger("GRABBER", "TryInterval", 1, configuration.GrabOptions.TryInterval) &&
		TryReadInteger("GRABBER", "ClicksPerTry", 2, configuration.GrabOptions.ClicksPerTry) &&
		TryReadInteger("SCANNER", "DefaultSensibility", 120, configuration.GrabOptions.InputOptions.sensibility) &&
		TryReadInteger("SCANNER", "SensibilityLevel", 10, configuration.GrabOptions.SensibilityLevel) &&
		TryReadInteger("SCANNER", "PixelsToScan", 2, configuration.GrabOptions.PixelsToScan) &&
		TryReadCoordinates("SCANNER", "Coordinates1", "950 - 757", configuration.GrabOptions.ScanCoordinates) &&
		TryReadMultipleCoordinates("SCANNER", configuration.GrabOptions.ScanCoordinates);
}

// Try to read a string from the ini file.
bool IniManager::TryReadString(const string& section, const string& key, const string& defaultValue, string& result)
{
	TCHAR* value = new TCHAR[255];
	memset(value, 0x00, 255);
	DWORD length = GetPrivateProfileStringW(_W(section).c_str(), _W(key).c_str(), _W(defaultValue).c_str(), value, 255, m_fileName.c_str());
	result = _N(value);
	delete[] value;
	return length != 0; 
}

// Try to read an integer from the ini file.
bool IniManager::TryReadInteger(const string& section, const string& key, const int& defaultValue, int& result)
{
	const int default = defaultValue == INT_MIN ? INT_MAX : INT_MIN;
	result = GetPrivateProfileIntW(_W(section).c_str(), _W(key).c_str(), default, m_fileName.c_str());
	if (result != default) return true;
	result = defaultValue;
	return false;
}

// Try to read coordinates from the ini file.
bool IniManager::TryReadCoordinates(const string& section, const string& key, const string& defaultValue, vector<POINT>& result)
{
	string value;
	return TryReadString(section, key, defaultValue, value) && TryReadCoordinates(value, result);
}

// Try to read coordinates from the ini file.
bool IniManager::TryReadCoordinates(const string& section, const string& key, const string& defaultValue, POINT& result)
{
	string value;
	return TryReadString(section, key, defaultValue, value) && TryReadCoordinates(value, result);
}

// Try to read multiple coordinates from the ini file.
bool IniManager::TryReadMultipleCoordinates(const string& section, vector<POINT>& result)
{
	int index = 2;
	ostringstream coordinate("Coordinates2");
	string key;
	while (TryReadString(section, coordinate.str().c_str(), "", key))
	{
		if (!TryReadCoordinates(key, result)) return false;
		coordinate.str("");
		coordinate << "Coordinates" << ++index;
	}
	return true;
}

// Try to read coordinates from a given value.
bool IniManager::TryReadCoordinates(const string& value, vector<POINT>& result)
{
	POINT point;
	if (!TryReadCoordinates(value, point)) return false;
	result.push_back(point);
	return true;
}

// Try to read multiple coordinates from a given value.
bool IniManager::TryReadCoordinates(const string& value, POINT& result)
{
	size_t found = value.find('-');
	if (found == string::npos) return LogManager::DisplayWarning(L"Could not found '-' on [Coordinates = CoordinateX - CoordinateY] line.\n");
	result.x = atoi(value.substr(0, found).c_str());
	result.y = atoi(value.substr(found + 1).c_str());
	return true;
}

// Removes all leading and trailing white-space characters from the value.
string IniManager::Trim(const string& value)
{
	string result = value;
	string whitespaces(" \t\f\v\n\r");

	// remove leading white-space charaters
	size_t found = result.find_first_not_of(whitespaces);
	if (found != string::npos)
		result.erase(0, found);
	else
		return "";

	// remove trailing white-space charaters
	found = result.find_last_not_of(whitespaces);
	if (found != string::npos)
		result.erase(found + 1);

	return result;
}